package com.huanzhidadi.java8inaction.chapter5;

import com.huanzhidadi.java8inaction.model.Dish;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
public class CollectTest {
    public static void main(String[] args) {

        List<Dish> menu = Arrays.asList(
                new Dish("pork", false, 800, Dish.Type.MEAT),
                new Dish("beef", false, 700, Dish.Type.MEAT),
                new Dish("season fruit", true, 120, Dish.Type.OTHER),
                new Dish("chicken", false, 400, Dish.Type.MEAT),
                new Dish("rice", true, 350, Dish.Type.OTHER),
                new Dish("pizza", true, 550, Dish.Type.OTHER),
                new Dish("prawns", false, 300, Dish.Type.FISH),
                new Dish("salmon", false, 450, Dish.Type.FISH)
        );

        // 归约和汇总
        Long dishCount = menu.stream().collect(Collectors.counting());
        log.info("菜品数量：{}", dishCount);

        Optional<Dish> optionalDish = menu.stream().collect(Collectors.maxBy(Comparator.comparing(Dish::getCalories)));
        log.info("热量最高的菜品：{}", optionalDish.get());

        Integer sumCalories = menu.stream().collect(Collectors.summingInt(Dish::getCalories));
        log.info("菜品热量总和：{}", sumCalories);

        String dishName = menu.stream().map(Dish::getName).collect(Collectors.joining());
        log.info("全部菜品名称：{}", dishName);

        String dishName2 = menu.stream().map(Dish::getName).collect(Collectors.joining(", "));
        log.info("全部菜品名称：{}", dishName2);

        // 分组
        Map<Dish.Type, List<Dish>> dishTypeGroup = menu.stream().collect(Collectors.groupingBy(Dish::getType));
        log.info("菜品分组：{}", dishTypeGroup);
        // 用Lambda表达式进行分组
        Map<Dish.CaloricLevel, List<Dish>> caloricGroup = menu.stream().collect(
                Collectors.groupingBy(
                        dish -> {
                            if (dish.getCalories() <= 400) {
                                return Dish.CaloricLevel.DIET;
                            } else if (dish.getCalories() <= 700) {
                                return Dish.CaloricLevel.NORMAL;
                            } else {
                                return Dish.CaloricLevel.FAT;
                            }
                        }
                )
        );
        log.info("菜品热量分组：{}", caloricGroup);

        // 二级分组
        Map<Dish.Type, Map<Dish.CaloricLevel, List<Dish>>> level2Group = menu.stream()
                .collect(
                        // 一级分类函数
                        Collectors.groupingBy(Dish::getType,
                                // 二级分类函数
                                Collectors.groupingBy(
                                        dish -> {
                                            if (dish.getCalories() <= 400) {
                                                return Dish.CaloricLevel.DIET;
                                            } else if (dish.getCalories() <= 700) {
                                                return Dish.CaloricLevel.NORMAL;
                                            } else {
                                                return Dish.CaloricLevel.FAT;
                                            }
                                        }
                                )));
        log.info("菜品二级分组：{}", level2Group);

        // 分组后counting
        Map<Dish.Type, Long> groupCount = menu.stream().collect(
                Collectors.groupingBy(Dish::getType, Collectors.counting())
        );
        log.info("菜品分组后counting：{}", groupCount);

        // 分组后maxBy
        Map<Dish.Type, Optional<Dish>> groupMax = menu.stream().collect(
                Collectors.groupingBy(Dish::getType, Collectors.maxBy(Comparator.comparingInt(Dish::getCalories)))
        );
        log.info("菜品分组后maxBy：{}", groupMax);

        // collectingAndThen
        Map<Dish.Type, Dish> groupMaxDish = menu.stream().collect(
                Collectors.groupingBy(Dish::getType,
                        Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparingInt(Dish::getCalories)), Optional::get))
        );
        log.info("菜品分组后groupMaxDish：{}", groupMaxDish);


        // 分区
        Map<Boolean, List<Dish>> partitionVegetarian = menu.stream().collect(
                Collectors.partitioningBy(Dish::isVegetarian)
        );
        log.info("菜品按是否素食分区：{}", partitionVegetarian);

        // 多级分区
        Map<Boolean, Map<Boolean, List<Dish>>> level2Partition = menu.stream().collect(
                Collectors.partitioningBy(Dish::isVegetarian,
                        Collectors.partitioningBy(d -> d.getCalories() > 500))
        );
        log.info("先按是否素食分区，再按是否热量大于500分区：{}", level2Partition);
    }
}
